<?php

namespace pUtils;

use Throwable;

class utils {
    
    /** 解析请求头不带”content-type:application/json“，但实际传输内容为json格式的字符串
     * @return mixed json_decode(file_get_contents("php://input") ?? "{}", true) ?? []
     */
    static function decodeInputJSON(): mixed {
        return json_decode(file_get_contents("php://input") ?? "{}", true) ?? [];
    }
    
    /** 生成api响应结构的json格式字符串
     * @param string $msg
     * @param int    $code
     * @param mixed  $data
     * @return false|string json_encode(["code" => $code, "msg" => $msg, "data" => $data])
     */
    static function respJSON(string $msg = "失败", int $code = 0, mixed $data = null): false|string {
        return json_encode(["code" => $code, "msg" => $msg, "data" => $data]);
    }
    
    /**
     * 深层获取array|object指定的多个key的值
     * @param mixed            $data
     * @param array|string|int ...$keys
     * @return array
     */
    static function getVals(mixed $data, ...$keys): array {
        return array_map(function ($data, $key) {
            return is_array($key) ? self::getVal($data, ...$key) : self::getVal($data, $key);
        }, array_fill(0, sizeof($keys), $data), $keys);
    }
    
    /**
     * 深层获取array|object指定key的值
     * @param mixed $data
     * @param mixed ...$keys
     * @return mixed
     */
    static function getVal(mixed $data = null, ...$keys): mixed {
        return $keys
            ? (is_array($data)
                ? self::getVal($data[$keys[0]] ?? null, ...array_slice($keys, 1))
                : (is_object($data)
                    ? self::getVal($data->$keys[0] ?? null, ...array_slice($keys, 1))
                    : null))
            : self::nvl($data);
    }
    
    /** 返回参数中第一个非空值
     * @param ...$args
     * @return mixed
     */
    public static function nvl(...$args): mixed {
        foreach ($args as $arg) {
            if (!self::isNull($arg)) {
                return $arg;
            }
        }
        
        return null;
    }
    
    /** 判断第几个参数为null或空字串，无满足参数时返回false
     * @param ...$args
     * @return false|int
     */
    public static function isNull(...$args): false|int {
        foreach (array_values($args) as $idx => $arg) {
            if (!isset($arg) || $arg === "") {
                return $idx + 1;
            }
        }
        
        return false;
    }
    
    /** 返回参数中第一个非null或非空字串或非空数组
     * @param ...$args
     * @return mixed
     */
    public static function evl(...$args): mixed {
        foreach ($args as $arg) {
            if (!self::isEmpty($arg)) {
                return $arg;
            }
        }
        
        return null;
    }
    
    /** 判断是否为null或空字串或空数组
     * @param ...$args
     * @return false|int
     */
    public static function isEmpty(...$args): false|int {
        foreach (array_values($args) as $idx => $arg) {
            if (!utils::isNumber($arg) && !($arg === false) && empty($arg)) {
                return $idx + 1;
            }
        }
        
        return false;
    }
    
    /** 判断第几个参数为整数或小数或整数字串/小数字串，无满足参数时返回false
     * @param ...$args
     * @return false|int
     */
    public static function isNumber(...$args): false|int {
        foreach (array_values($args) as $idx => $arg) {
            if (self::isInt($arg) || self::isFloat($arg)) {
                return $idx + 1;
            }
        }
        
        return false;
    }
    
    /** 判断第几个参数为整数，无满足参数时返回false
     * @param ...$args
     * @return false|int
     */
    public static function isInt(...$args): false|int {
        foreach (array_values($args) as $idx => $arg) {
            if (is_string($arg) ? ctype_digit($arg) : is_int($arg)) {
                return $idx + 1;
            }
        }
        
        return false;
    }
    
    /** 判断第几个参数为浮点数，无满足参数时返回false
     * @param ...$args
     * @return false|int
     */
    public static function isFloat(...$args): false|int {
        foreach (array_values($args) as $idx => $arg) {
            if (is_string($arg) ? !!preg_match('/^[+-]?(\d+(\.\d*))$/', $arg) : is_float($arg)) {
                return $idx + 1;
            }
        }
        
        return false;
    }
    
    /** 判断参数是否全部为null或空字串
     * @param ...$args
     * @return bool
     */
    public static function isAllNull(...$args): bool {
        return !array_filter($args, function ($arg) {
            return !self::isNull($arg);
        });
    }
    
    /** 判断参数是否全部为null或空字串或空数组
     * @param ...$args
     * @return bool
     */
    public static function isAllEmpty(...$args): bool {
        return !array_filter($args, function ($arg) {
            return !self::isEmpty($arg);
        });
    }
    
    /** 判断参数是否全部为浮点数
     * @param ...$args
     * @return bool
     */
    public static function isAllFloat(...$args): bool {
        return !array_filter($args, function ($arg) {
            return !self::isFloat($arg);
        });
    }
    
    /** 判断参数是否全部为整数
     * @param ...$args
     * @return bool
     */
    public static function isAllInt(...$args): bool {
        return !array_filter($args, function ($arg) {
            return !self::isInt($arg);
        });
    }
    
    /** 判断参数是否全部为整数或小数或整数字串/小数字串
     * @param ...$args
     * @return bool
     */
    public static function isAllNumber(...$args): bool {
        return !array_filter($args, function ($arg) {
            return !self::isNumber($arg);
        });
    }
    
    /** 判断参数是否全部为手机号
     * @param ...$args
     * @return bool
     */
    public static function isAllMobile(...$args): bool {
        return !array_filter($args, function ($arg) {
            return !self::isMobile($arg);
        });
    }
    
    /** 判断第几个参数为手机号，无满足参数时返回false
     * @param ...$args
     * @return false|int
     */
    public static function isMobile(...$args): false|int {
        foreach (array_values($args) as $idx => $arg) {
            if (!!preg_match('/^1[345789]\d{9}$/', $arg)) {
                return $idx + 1;
            }
        }
        
        return false;
    }
    
    /** 判断第几个参数为邮箱，无满足参数时返回false
     * @param ...$args
     * @return false|int
     */
    public static function isEmail(...$args): false|int {
        return self::filterVal(FILTER_VALIDATE_EMAIL, ...$args);
    }
    
    /** 判断第几个参数满足filter_val()，无满足参数时返回false
     * @param int $filter
     * @param     ...$args
     * @return bool|int
     */
    public static function filterVal(int $filter, ...$args): bool|int {
        foreach (array_values($args) as $idx => $arg) {
            if (filter_var($arg, $filter)) {
                return $idx + 1;
            }
        }
        
        return false;
    }
    
    /** 判断参数是否全部为邮箱
     * @param ...$args
     * @return bool
     */
    public static function isAllEmail(...$args): bool {
        return self::filterValAll(FILTER_VALIDATE_EMAIL, ...$args);
    }
    
    /** 判断参数是否全部满足filter_val()
     * @param int $filter
     * @param     ...$args
     * @return bool
     */
    public static function filterValAll(int $filter, ...$args): bool {
        return !array_filter($args, function ($arg) use ($filter) {
            return !self::filterVal($filter, $arg);
        });
    }
    
    /** 判断第几个参数为链接，无满足参数时返回false
     * @param ...$args
     * @return false|int
     */
    public static function isUrl(...$args): false|int {
        return self::filterVal(FILTER_VALIDATE_URL, ...$args);
    }
    
    /** 判断参数是否全部为链接
     * @param ...$args
     * @return bool
     */
    public static function isAllUrl(...$args): bool {
        return self::filterValAll(FILTER_VALIDATE_URL, ...$args);
    }
    
    /** 判断第几个参数为ip，无满足参数时返回false
     * @param ...$args
     * @return false|int
     */
    public static function isIp(...$args): false|int {
        return self::filterVal(FILTER_VALIDATE_IP, ...$args);
    }
    
    /** 判断参数是否全部为ip
     * @param ...$args
     * @return bool
     */
    public static function isAllIp(...$args): bool {
        return self::filterValAll(FILTER_VALIDATE_IP, ...$args);
    }
    
    /** 判断第几个参数为Mac，无满足参数时返回false
     * @param ...$args
     * @return false|int
     */
    public static function isMac(...$args): false|int {
        return self::filterVal(FILTER_VALIDATE_MAC, ...$args);
    }
    
    /** 判断参数是否全部为Mac
     * @param ...$args
     * @return bool
     */
    public static function isAllMac(...$args): bool {
        return self::filterValAll(FILTER_VALIDATE_MAC, ...$args);
    }
    
    /** 判断参数是否全部为日期或时间
     * @param                $args
     * @param mixed|string[] $formats
     * @return bool
     */
    public static function isAllDate($args, mixed $formats = null): bool {
        return !array_filter($args, function ($arg) use ($formats) {
            return !self::isDate($arg, $formats);
        });
    }
    
    /** 判断第几个参数为日期或时间，无满足参数时返回false
     * @param                $args
     * @param mixed|string[] $formats
     * @return false|int
     */
    public static function isDate($args, mixed $formats = ["Y-m-d", "Y-m-d H:i", "Y-m-d H:i:s", "Y/m/d", "Y/m/d H:i", "Y/m/d H:i:s", "H:i", "H:i:s"]): false|int {
        if (is_string($args)) {
            $args = [$args];
        } else if (!is_array($args)) {
            return false;
        }
        
        if (is_string($formats = $formats ?? ["Y-m-d", "Y-m-d H:i", "Y-m-d H:i:s", "Y/m/d", "Y/m/d H:i", "Y/m/d H:i:s", "H:i", "H:i:s"])) {
            $formats = [$formats];
        } else if (!is_array($formats)) {
            return false;
        }
        
        foreach (array_values($args) as $idx => $arg) {
            if (is_string($arg) && ($time = strtotime($arg))
                && array_filter($formats, function ($format) use ($arg, $time) {
                    return is_string($format) && date($format, $time) === $arg;
                })) {
                return $idx + 1;
            }
        }
        
        return false;
    }
    
    public static function showErrMsg(Throwable $e) {
        $code    = $e->getCode();
        $file    = $e->getFile();
        $line    = $e->getLine();
        $message = $e->getMessage();
        
        echo "[code:$code] [file:$file] [line:$line] [message:$message]";
    }
}